/*******************************************************************************
 * (c) Copyright 2011 Actel Corporation.  All rights reserved.
 *
 * G4Main Microcontroller Subsystem UART bare metal software driver
 * implementation.
 *
 * SVN $Revision: 4578 $
 * SVN $Date: 2012-08-28 12:31:47 +0100 (Tue, 28 Aug 2012) $
 */
#include "mss_uart.h"
#include "mss_uart_regs.h"
#include "../../CMSIS/mss_assert.h"
#include "../../CMSIS/hw_reg_io.h"
#include "../../CMSIS/system_m2sxxx.h"

#ifdef __cplusplus
extern "C" {
#endif 

/*******************************************************************************
 * Defines
 */
#define TX_COMPLETE		       0U
#define TX_FIFO_SIZE	       16U

#define FCR_TRIG_LEVEL_MASK    0xC0U

#define IIRF_MASK              0x0FU

#define INVALID_INTERRUPT      0U
#define INVALID_IRQ_HANDLER    ( (mss_uart_irq_handler_t) 0 )
#define NULL_HANDLER           ( (mss_uart_irq_handler_t) 0 )

#define MSS_UART_DATA_READY    ( (uint8_t) 0x01 )

#define SYNC_ASYNC_MODE_MASK   (0x7U)

/*******************************************************************************
 * Possible values for Interrupt Identification Register Field.
 */
#define IIRF_MODEM_STATUS   0x00U    
#define IIRF_THRE           0x02U
#define IIRF_MMI            0x03U
#define IIRF_RX_DATA        0x04U
#define IIRF_RX_LINE_STATUS 0x06U
#define IIRF_DATA_TIMEOUT   0x0CU

/*******************************************************************************
 * Receiver error status mask.
 */
#define STATUS_ERROR_MASK    ( MSS_UART_OVERUN_ERROR | MSS_UART_PARITY_ERROR | \
                               MSS_UART_FRAMING_ERROR  | MSS_UART_BREAK_ERROR | \
                               MSS_UART_FIFO_ERROR)

/*******************************************************************************
 * Cortex-M3 interrupt handler functions implemented as part of the MSS UART
 * driver.
 */
#if defined(__GNUC__)
__attribute__((__interrupt__)) void UART0_IRQHandler( void );
#else
void UART0_IRQHandler( void );
#endif

#if defined(__GNUC__)
__attribute__((__interrupt__)) void UART1_IRQHandler( void );
#else
void UART1_IRQHandler( void );
#endif

/*******************************************************************************
 * Local functions.
 */
static void global_init(mss_uart_instance_t * this_uart, uint32_t baud_rate, 
                        uint8_t line_config);
static void MSS_UART_isr( mss_uart_instance_t * this_uart );
static void default_tx_handler( mss_uart_instance_t * this_uart );

static void config_baud_divisors
(
    mss_uart_instance_t * this_uart,
    uint32_t baudrate    
);

/*******************************************************************************
 * Instance definitions
 */
mss_uart_instance_t g_mss_uart0;
mss_uart_instance_t g_mss_uart1;


/*******************************************************************************
 * Public Functions
 *******************************************************************************/
/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_init
(
	mss_uart_instance_t* this_uart, 
    uint32_t baud_rate,
    uint8_t line_config
)
{
    /* The driver expects g_mss_uart0 and g_mss_uart1 to be the only
     * mss_uart_instance_t instances used to identify UART0 and UART1. */
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    /* Perform generic initialization */
    global_init( this_uart, baud_rate, line_config );

    /* Disable LIN mode */
    clear_bit_reg8(&this_uart->hw_reg->MM0, ELIN);

    /* Disable IrDA mode */
    clear_bit_reg8(&this_uart->hw_reg->MM1, EIRD);

    /* Disable SmartCard Mode */
    clear_bit_reg8(&this_uart->hw_reg->MM2, EERR);

    /* set default tx handler for automated TX using interrupt in USART mode */
    this_uart->tx_handler       = default_tx_handler;
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void MSS_UART_lin_init
(
    mss_uart_instance_t* this_uart, 
    uint32_t baud_rate,
    uint8_t line_config
)
{
    /* The driver expects g_mss_uart0 and g_mss_uart1 to be the only
     * mss_uart_instance_t instances used to identify UART0 and UART1. */
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    /* Perform generic initialization */
    global_init( this_uart, baud_rate, line_config );

     /* Enable LIN mode */
    set_bit_reg8(&this_uart->hw_reg->MM0, ELIN);

    /* Disable IrDA mode */
    clear_bit_reg8(&this_uart->hw_reg->MM1, EIRD);

    /* Disable SmartCard Mode */
    clear_bit_reg8(&this_uart->hw_reg->MM2, EERR);
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_irda_init
(
    mss_uart_instance_t* this_uart, 
    uint32_t baud_rate,
    uint8_t line_config,
    mss_uart_rzi_polarity_t rxpol,
    mss_uart_rzi_polarity_t txpol,
    mss_uart_rzi_pulsewidth_t pw
)
{
    /* The driver expects g_mss_uart0 and g_mss_uart1 to be the only
     * mss_uart_instance_t instances used to identify UART0 and UART1. */
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    /* Perform generic initialization */
    global_init( this_uart, baud_rate, line_config );

     /* Enable LIN mode */
    clear_bit_reg8(&this_uart->hw_reg->MM0, ELIN);

    /* Disable IrDA mode */
    set_bit_reg8(&this_uart->hw_reg->MM1, EIRD);
    ( (rxpol == MSS_UART_ACTIVE_LOW) ? clear_bit_reg8(&this_uart->hw_reg->MM1,EIRX) : 
                                       set_bit_reg8(&this_uart->hw_reg->MM1,EIRX) );
    ( (txpol == MSS_UART_ACTIVE_LOW) ? clear_bit_reg8(&this_uart->hw_reg->MM1,EITX) : 
                                       set_bit_reg8(&this_uart->hw_reg->MM1,EITX) );
    ( (pw == MSS_UART_ACTIVE_LOW) ? clear_bit_reg8(&this_uart->hw_reg->MM1,EITP) : 
                                       set_bit_reg8(&this_uart->hw_reg->MM1,EITP) );    
    /* Disable SmartCard Mode */
    clear_bit_reg8(&this_uart->hw_reg->MM2, EERR);
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_smartcard_init
(
    mss_uart_instance_t* this_uart, 
    uint32_t baud_rate,
    uint8_t line_config
)
{
    /* The driver expects g_mss_uart0 and g_mss_uart1 to be the only
     * mss_uart_instance_t instances used to identify UART0 and UART1. */
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    /* Perform generic initialization */
    global_init( this_uart, baud_rate, line_config );
    
    /* Disable LIN mode */
    clear_bit_reg8(&this_uart->hw_reg->MM0, ELIN);

    /* Disable IrDA mode */
    clear_bit_reg8(&this_uart->hw_reg->MM1, EIRD);

    /* Enable SmartCard Mode : Only when data is 8-bit and 2 stop bits*/
    if( ( MSS_UART_DATA_8_BITS | MSS_UART_TWO_STOP_BITS) == 
        (line_config & (MSS_UART_DATA_8_BITS | MSS_UART_TWO_STOP_BITS)) )
    {
        set_bit_reg8(&this_uart->hw_reg->MM2, EERR);    
        /* Enable single wire half-duplex mode */
        set_bit_reg8(&this_uart->hw_reg->MM2,ESWM);
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_polled_tx
(
    mss_uart_instance_t * this_uart,
    const uint8_t * pbuff,
    uint32_t tx_size
)
{
    uint32_t char_idx = 0U;
    uint32_t size_sent;
    uint8_t status;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( pbuff != ( (uint8_t *)0 ) );
    ASSERT( tx_size > 0U );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
          ( pbuff != ( (uint8_t *)0 ) ) && ( tx_size > 0U ) )
    {
         /* Remain in this loop until the entire input buffer
          * has been transferred to the UART.
          */
        do {
            /* Read the Line Status Register and update the sticky record */
            status = this_uart->hw_reg->LSR;
            this_uart->status |= status;

            /* Check if TX FIFO is empty. */
            if( status & MSS_UART_THRE )
            {
                uint32_t fill_size = TX_FIFO_SIZE;

                /* Calculate the number of bytes to transmit. */
                if ( tx_size < TX_FIFO_SIZE )
                {
                    fill_size = tx_size;
                }

                /* Fill the TX FIFO with the calculated the number of bytes. */
                for ( size_sent = 0U; size_sent < fill_size; ++size_sent )
                {
                    /* Send next character in the buffer. */
                    this_uart->hw_reg->THR = pbuff[char_idx++];
                }

                /* Calculate the number of untransmitted bytes remaining. */
                tx_size -= size_sent;
            }
        }while( tx_size );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_polled_tx_string
(
    mss_uart_instance_t * this_uart,
    const uint8_t * p_sz_string
)
{
    uint32_t char_idx = 0U;
    uint32_t fill_size;
    uint_fast8_t data_byte;
    uint8_t status;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( p_sz_string != ( (uint8_t *)0 ) );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
          ( p_sz_string != ( (uint8_t *)0 ) ) )
    {
        /* Get the first data byte from the input buffer */
        data_byte = (uint_fast8_t)p_sz_string[char_idx];

        /* First check for the NULL terminator byte.
         * Then remain in this loop until the entire string in the input buffer
         * has been transferred to the UART.
         */
        while ( 0U != data_byte )
        {
            /* Wait until TX FIFO is empty. */
            do {
                status = this_uart->hw_reg->LSR;
                this_uart->status |= status;
            } while ( !( status & MSS_UART_THRE ) );

            /* Send bytes from the input buffer until the TX FIFO is full
             * or we reach the NULL terminator byte.
             */
            fill_size = 0U;
            while ( (0U != data_byte) && (fill_size < TX_FIFO_SIZE) )
            {
                /* Send the data byte */
                this_uart->hw_reg->THR = data_byte;
                ++fill_size;
                char_idx++;
                /* Get the next data byte from the input buffer */
                data_byte = (uint_fast8_t)p_sz_string[char_idx];
            }
        }
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_irq_tx
(
    mss_uart_instance_t * this_uart,
    const uint8_t * pbuff,
    uint32_t tx_size
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( pbuff != ((uint8_t *)0) );
    ASSERT( tx_size > 0U );

    if ( ( tx_size > 0U ) && ( pbuff != ((uint8_t *)0) ) &&
       ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) )
    {
        /*Initialise the transmit info for the UART instance with the arguments.*/
        this_uart->tx_buffer = pbuff;
        this_uart->tx_buff_size = tx_size;
        this_uart->tx_idx = (uint16_t)0;

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* assign default handler for data transfer */
        this_uart->tx_handler = default_tx_handler;

        /* enables TX interrupt */
        set_bit_reg8(&this_uart->hw_reg->IER,ETBEI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
int8_t
MSS_UART_tx_complete
(
    mss_uart_instance_t * this_uart
)
{
    int8_t ret_value = 0;
    uint8_t status = 0U;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /* Read the Line Status Register and update the sticky record. */
        status = this_uart->hw_reg->LSR;
        this_uart->status |= status;

        if ( ( TX_COMPLETE == this_uart->tx_buff_size ) &&
             ( status & MSS_UART_TEMT ) )
        {
            ret_value = (int8_t)1;
        }
    }
    return ret_value;
}


/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
size_t
MSS_UART_get_rx
(
    mss_uart_instance_t * this_uart,
    uint8_t * rx_buff,
    size_t buff_size
)
{
    size_t rx_size = 0U;
    uint8_t status = 0U;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( rx_buff != ((uint8_t *)0) );
    ASSERT( buff_size > 0U );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
          ( rx_buff != ((uint8_t *)0) ) && ( buff_size > 0U ) )
    {
        status = this_uart->hw_reg->LSR;
        this_uart->status |= status;

        while (( (status & MSS_UART_DATA_READY) != 0U) &&
               ( rx_size < buff_size ) )
        {
            rx_buff[rx_size] = this_uart->hw_reg->RBR;
            ++rx_size;
            status = this_uart->hw_reg->LSR;
            this_uart->status |= status;
        }
    }
    return rx_size;
}
/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_enable_irq
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_t irq_mask
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( MSS_UART_INVALID_IRQ > irq_mask );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) && 
        ( MSS_UART_INVALID_IRQ > irq_mask ) )
    {
        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* irq_mask encoding: 1- enable
         * bit 0 - Receive Data Available Interrupt
         * bit 1 - Transmitter Holding  Register Empty Interrupt
         * bit 2 - Receiver Line Status Interrupt
         * bit 3 - Modem Status Interrupt
         */
        this_uart->hw_reg->IER |= (uint8_t)( irq_mask & IIRF_MASK );

        /* 
         * bit 4 - Receiver time-out interrupt
         * bit 5 - NACK / ERR signal interrupt
         * bit 6 - PID parity error interrupt 
         * bit 7 - LIN break detection interrupt
         * bit 8 - LIN Sync detection interrupt
         */
        this_uart->hw_reg->IEM |= (uint8_t)(( irq_mask & ~IIRF_MASK ) >> 8U);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_disable_irq
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_t irq_mask
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /* irq_mask encoding: 1 - disable
         * bit 0 - Receive Data Available Interrupt
         * bit 1 - Transmitter Holding  Register Empty Interrupt
         * bit 2 - Receiver Line Status Interrupt
         * bit 3 - Modem Status Interrupt
         */
        this_uart->hw_reg->IER &= ( (uint8_t)(~(irq_mask & IIRF_MASK)));

        /* 
         * bit 4 - Receiver time-out interrupt
         * bit 5 - NACK / ERR signal interrupt
         * bit 6 - PID parity error interrupt 
         * bit 7 - LIN break detection interrupt
         * bit 8 - LIN Sync detection interrupt
         */
        this_uart->hw_reg->IEM |= (uint8_t)(~((irq_mask & ~IIRF_MASK) >> 8U));

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        if( irq_mask == IIRF_MASK )
        {
            /* Disable UART instance interrupt in Cortex-M3 NVIC. */
            NVIC_DisableIRQ( this_uart->irqn );

        }
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_rx_handler
(
    mss_uart_instance_t *       this_uart,
    mss_uart_irq_handler_t      handler,
    mss_uart_rx_trig_level_t    trigger_level
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( handler != INVALID_IRQ_HANDLER );
    ASSERT( trigger_level < MSS_UART_FIFO_INVALID_TRIG_LEVEL );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( handler != INVALID_IRQ_HANDLER) &&
        ( trigger_level < MSS_UART_FIFO_INVALID_TRIG_LEVEL) )
    {
        this_uart->rx_handler = handler;

        /* Set the receive interrupt trigger level. */
        this_uart->hw_reg->FCR = (this_uart->hw_reg->FCR &
                              (uint8_t)(~((uint8_t)FCR_TRIG_LEVEL_MASK))) |
                              (uint8_t)trigger_level;
        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* Enable receive interrupt. */
        set_bit_reg8(&this_uart->hw_reg->IER,ERBFI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_loopback
(
    mss_uart_instance_t *   this_uart,
    mss_uart_loopback_t     loopback
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( MSS_UART_INVALID_LOOPBACK > loopback );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) ||
        ( MSS_UART_INVALID_LOOPBACK > loopback ) )
    {
        switch( loopback )
        {
            case MSS_UART_LOCAL_LOOPBACK_OFF:
                /* Disable local loopback */
                clear_bit_reg8(&this_uart->hw_reg->MCR,LOOP);
                break;
            case MSS_UART_LOCAL_LOOPBACK_ON:
                /* Enable local loopback */
                set_bit_reg8(&this_uart->hw_reg->MCR,LOOP);
                break;
            case MSS_UART_REMOTE_LOOPBACK_OFF:
            case MSS_UART_AUTO_ECHO_OFF:
                /* Disable remote loopback & automatic echo*/
                this_uart->hw_reg->MCR &= ~RLOOP_MASK;
                break;
            case MSS_UART_REMOTE_LOOPBACK_ON:
                /* Enable remote loopback */
                this_uart->hw_reg->MCR |= (1u << RLOOP);
            case MSS_UART_AUTO_ECHO_ON:
                /* Enable automatic echo */
                this_uart->hw_reg->MCR |= (1u << ECHO);
                break;
                
            case MSS_UART_INVALID_LOOPBACK:
                /* Fall through to default. */
            default:
                ASSERT(0);
                break;
        }
    }
}

/***************************************************************************//**
 * UART0 interrupt service routine.
 * UART0_IRQHandler is included within the Cortex-M3 vector table as part of the
 * Fusion 2 CMSIS.
 */
#if defined(__GNUC__)
__attribute__((__interrupt__)) void UART0_IRQHandler( void )
#else
void UART0_IRQHandler( void )
#endif
{
    MSS_UART_isr( &g_mss_uart0 );
    NVIC_ClearPendingIRQ( UART0_IRQn );
}

/***************************************************************************//**
 * UART1 interrupt service routine.
 * UART2_IRQHandler is included within the Cortex-M3 vector table as part of the
 * Fusion 2 CMSIS.
 */
#if defined(__GNUC__)
__attribute__((__interrupt__)) void UART1_IRQHandler( void )
#else
void UART1_IRQHandler( void )
#endif
{
    MSS_UART_isr( &g_mss_uart1 );
    NVIC_ClearPendingIRQ( UART1_IRQn );
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_rxstatus_handler
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_handler_t handler
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( handler != INVALID_IRQ_HANDLER);

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( handler != INVALID_IRQ_HANDLER) )
    {
        this_uart->linests_handler = handler;

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* Enable receiver line status interrupt. */
        set_bit_reg8(&this_uart->hw_reg->IER,ELSI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_tx_handler
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_handler_t handler
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( handler != INVALID_IRQ_HANDLER);

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( handler != INVALID_IRQ_HANDLER) )
    {
        this_uart->tx_handler = handler;

        /* Make TX buffer info invalid */
        this_uart->tx_buffer = (const uint8_t *)0;
        this_uart->tx_buff_size = 0U;

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* Enable transmitter holding register Empty interrupt. */
        set_bit_reg8(&this_uart->hw_reg->IER,ETBEI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_modemstatus_handler
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_handler_t handler
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( handler != INVALID_IRQ_HANDLER );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( handler != INVALID_IRQ_HANDLER) )
    {
        this_uart->modemsts_handler = handler;

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* Enable modem status interrupt. */
        set_bit_reg8(&this_uart->hw_reg->IER,EDSSI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
size_t
MSS_UART_fill_tx_fifo
(
    mss_uart_instance_t * this_uart,
    const uint8_t * tx_buffer,
    size_t tx_size
)
{
    uint8_t status = 0U;
    size_t size_sent = 0U;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( tx_buffer != ( (uint8_t *)0 ) );
    ASSERT( tx_size > 0 );

    /* Fill the UART's Tx FIFO until the FIFO is full or the complete input
     * buffer has been written. */
    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1 ) ) &&
        (tx_buffer != ( (uint8_t *)0 ))   &&
        (tx_size > 0u) )
    {
        status = this_uart->hw_reg->LSR;
        this_uart->status |= status;

        if( status & MSS_UART_THRE )
        {
            uint32_t fill_size = TX_FIFO_SIZE;

            if ( tx_size < TX_FIFO_SIZE )
            {
                fill_size = tx_size;
            }
            /* Fill up FIFO */
            for ( size_sent = 0U; size_sent < fill_size; ++size_sent )
            {

                /* Send next character in the buffer. */
                this_uart->hw_reg->THR = tx_buffer[size_sent];
            }
        }
    }
    return size_sent;
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
uint8_t
MSS_UART_get_rx_status
(
    mss_uart_instance_t * this_uart
)
{
    uint8_t status = MSS_UART_INVALID_PARAM;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /*
         * Extract UART receive error status.
         * Bit 1 - Overflow error status
         * Bit 2 - Parity error status
         * Bit 3 - Frame error status
         * Bit 4 - Break interrupt indicator
         * Bit 7 - FIFO data error status
         */
        this_uart->status |= (this_uart->hw_reg->LSR);
        status = (this_uart->status & STATUS_ERROR_MASK );
        /* Clear the sticky status after reading */
        this_uart->status = 0U;
    }
    return status;
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
uint8_t
MSS_UART_get_modem_status
(
    mss_uart_instance_t * this_uart
)
{
    uint8_t status = MSS_UART_INVALID_PARAM;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /*
         * Extract UART modem status and place in lower bits of "status".
         * Bit 0 - Delta Clear to Send Indicator
         * Bit 1 - Delta Clear to Receive Indicator
         * Bit 2 - Trailing edge of Ring Indicator detector
         * Bit 3 - Delta Data Carrier Detect indicator
         * Bit 4 - Clear To Send
         * Bit 5 - Data Set Ready
         * Bit 6 - Ring Indicator
         * Bit 7 - Data Carrier Detect
         */
        status = this_uart->hw_reg->MSR;
    }
    return status;
}

/***************************************************************************//**
 * MSS_UART_get_tx_status.
 * See mss_uart.h for details of how to use this function.
 */
uint8_t
MSS_UART_get_tx_status
(
    mss_uart_instance_t * this_uart
)
{
    uint8_t status = MSS_UART_TX_BUSY;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /* Read the Line Status Register and update the sticky record. */
        status = this_uart->hw_reg->LSR;
        this_uart->status |= status;
        /*
         * Extract the transmit status bits from the UART's Line Status Register.
         * Bit 5 - Transmitter Holding Register/FIFO Empty (THRE) status. (If = 1, TX FIFO is empty)
         * Bit 6 - Transmitter Empty (TEMT) status. (If = 1, both TX FIFO and shift register are empty)
         */
        status &= ( MSS_UART_THRE | MSS_UART_TEMT );
    }
    return status;
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_break
(
    mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
        /* set break charecter on Tx line */
        set_bit_reg8(&this_uart->hw_reg->LCR,SB);
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_clear_break
(
    mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
        /* remove break charecter from Tx line */
        clear_bit_reg8(&this_uart->hw_reg->LCR,SB);
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_pidpei_handler
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_handler_t handler
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( handler != INVALID_IRQ_HANDLER );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( handler != INVALID_IRQ_HANDLER) )
    {
        this_uart->pid_pei_handler = handler;

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* Enable PID parity error interrupt. */
        set_bit_reg8(&this_uart->hw_reg->IEM,EPID_PEI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_linbreak_handler
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_handler_t handler
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( handler != INVALID_IRQ_HANDLER );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( handler != INVALID_IRQ_HANDLER) )
    {
        this_uart->break_handler = handler;

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* Enable LIN break detection interrupt. */
        set_bit_reg8(&this_uart->hw_reg->IEM,ELINBI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_linsync_handler
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_handler_t handler
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( handler != INVALID_IRQ_HANDLER );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( handler != INVALID_IRQ_HANDLER) )
    {
        this_uart->break_handler = handler;

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* Enable LIN sync detection interrupt. */
        set_bit_reg8(&this_uart->hw_reg->IEM,ELINSI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_nack_handler
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_handler_t handler
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( handler != INVALID_IRQ_HANDLER );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( handler != INVALID_IRQ_HANDLER) )
    {
        this_uart->nack_handler = handler;

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* Enable LIN sync detection interrupt. */
        set_bit_reg8(&this_uart->hw_reg->IEM,ENACKI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_rx_timeout_handler
(
    mss_uart_instance_t * this_uart,
    mss_uart_irq_handler_t handler
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( handler != INVALID_IRQ_HANDLER );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( handler != INVALID_IRQ_HANDLER) )
    {
        this_uart->rto_handler = handler;

        /* Clear any previously pended interrupts */
        NVIC_ClearPendingIRQ( this_uart->irqn );

        /* Enable receiver timeout interrupt. */
        set_bit_reg8(&this_uart->hw_reg->IEM,ERTOI);

        /* Enable UART instance interrupt in Cortex-M3 NVIC. */
        NVIC_EnableIRQ( this_uart->irqn );
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_enable_half_duplex
(
    mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
        /* enable single wire half-duplex mode */
        set_bit_reg8(&this_uart->hw_reg->MM2,ESWM);
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_disable_half_duplex
(
    mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
        /* enable single wire half-duplex mode */
        clear_bit_reg8(&this_uart->hw_reg->MM2,ESWM);
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_rx_endian
(
    mss_uart_instance_t * this_uart,
    mss_uart_endian_t endian    
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( INVALID_ENDIAN > endian );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) && 
        ( INVALID_ENDIAN > endian ) )
    {
        /* Configure MSB first / LSB first for receiver */
        ( (MSS_UART_LITTLEEND == endian) ? (clear_bit_reg8(&this_uart->hw_reg->MM1,E_MSB_RX)) :
                                           (set_bit_reg8(&this_uart->hw_reg->MM1,E_MSB_RX)) ) ;
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_tx_endian
(
    mss_uart_instance_t * this_uart,
    mss_uart_endian_t endian    
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( INVALID_ENDIAN > endian );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) && 
        ( INVALID_ENDIAN > endian ) )
    {
        /* Configure MSB first / LSB first for transmitter */
        ( (MSS_UART_LITTLEEND == endian) ? (clear_bit_reg8(&this_uart->hw_reg->MM1,E_MSB_TX)) :
                                           (set_bit_reg8(&this_uart->hw_reg->MM1,E_MSB_TX)) ) ;
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_filter_length
(
    mss_uart_instance_t * this_uart,
    mss_uart_filter_length_t length
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( INVALID_FILTER_LENGTH > length );
    
    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) && 
        ( INVALID_FILTER_LENGTH > length ) )
    {
        /* Configure glitch filter length */
        this_uart->hw_reg->GLR = length;
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_enable_afm
(
     mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /* Disable RX FIFO till address flag with correct address is received */
        set_bit_reg8(&this_uart->hw_reg->MM2,EAFM);
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_disable_afm
(
     mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /* Enable RX FIFO irrespective of address flag and
           correct address is received */
        clear_bit_reg8(&this_uart->hw_reg->MM2,EAFM);
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_enable_afclear
(
     mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /* Enable address flag clearing */
        /* Disable RX FIFO till another address flag with 
           correct address is received */
        set_bit_reg8(&this_uart->hw_reg->MM2,EAFC);
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_disable_afclear
(
     mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /* Disable address flag clearing */
        clear_bit_reg8(&this_uart->hw_reg->MM2,EAFC);
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_enable_rx_timeout
(
    mss_uart_instance_t * this_uart,
    uint8_t timeout
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /* Load the receive timeout value */
        this_uart->hw_reg->RTO = timeout;
        /*Enable receiver time-out */
        set_bit_reg8(&this_uart->hw_reg->MM0,ERTO);
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_disable_rx_timeout
(
    mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /*Disable receiver time-out */
        clear_bit_reg8(&this_uart->hw_reg->MM0,ERTO);
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_enable_tx_time_guard
(
    mss_uart_instance_t * this_uart,
    uint8_t timeguard
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /* Load the transmitter time guard value */
        this_uart->hw_reg->TTG = timeguard;
        /*Enable transmitter time guard */
        set_bit_reg8(&this_uart->hw_reg->MM0,ETTG);
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_disable_tx_time_guard
(
    mss_uart_instance_t * this_uart
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        /*Disable transmitter time guard */
        clear_bit_reg8(&this_uart->hw_reg->MM0,ETTG);
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void
MSS_UART_set_address
(
    mss_uart_instance_t * this_uart,
    uint8_t address
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        this_uart->hw_reg->ADR = address;
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_set_ready_mode
(
    mss_uart_instance_t * this_uart,
    mss_uart_ready_mode_t mode    
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( INVALID_READY_MODE > mode );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( INVALID_READY_MODE > mode ) )
    {
        /* Configure mode 0 or mode 1 for TXRDY and RXRDY */
        ( ( READY_MODE0 == mode ) ? clear_bit_reg8(&this_uart->hw_reg->FCR,RDYMODE) :
                                    set_bit_reg8(&this_uart->hw_reg->FCR,RDYMODE) );    
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
#if 0
/* 
    This function does not make sense to be part of the driver API. The driver
    will make use of fractional baud divisors when possible to achieve most
    accurate baud rate.
*/
void
MSS_UART_config_fractional_baud
(
    mss_uart_instance_t * this_uart,
    uint32_t baudrate    
)
{
    uint32_t baud_value;
    uint32_t baud_value_by_64;
    uint32_t baud_value_by_128;
    uint32_t fractional_baud_value;
    uint32_t pclk_freq;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    
    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        this_uart->baudrate = baudrate;

         /* Force the value of the CMSIS global variables holding the various system
          * clock frequencies to be updated. */
         SystemCoreClockUpdate();
         pclk_freq = g_FrequencyPCLK0;

        /*
         * Compute baud value based on requested baud rate and PCLK frequency.
         * The baud value is computed using the following equation:
         *      baud_value = PCLK_Frequency / (baud_rate * 16)
         */
        baud_value_by_128 = (8u * pclk_freq) / baudrate;
        baud_value_by_64 = baud_value_by_128 / 2u;
        baud_value = baud_value_by_64 / 64u;
        fractional_baud_value = baud_value_by_64 - (baud_value * 64u);
        fractional_baud_value += baud_value_by_128 - (baud_value * 128u) - (fractional_baud_value * 2);
        
        /* Assert if integer baud value fits in 16-bit and greater than one */
        ASSERT( UINT16_MAX >= ((uint32_t)baud_value) );
        ASSERT(((uint32_t)baud_value) > 1U );
    
        if( UINT16_MAX >= baud_value > 1U )
        {
            /* set divisor latch */
            set_bit_reg8(&this_uart->hw_reg->LCR,DLAB);
        
            /* msb of baud value */
            this_uart->hw_reg->DMR = (uint8_t)(baud_value >> 8);
            /* lsb of baud value */
            this_uart->hw_reg->DLR = (uint8_t)baud_value;
        
            /* reset divisor latch */
            clear_bit_reg8(&this_uart->hw_reg->LCR,DLAB);
    
            /* Enable Fractional baud rate */
            set_bit_reg8(&this_uart->hw_reg->MM0,EFBR);
    
            /* Load the fractional baud rate register */
            this_uart->hw_reg->FBR = fractional_baud_value;
        }
    }
}
#endif

/***************************************************************************//**
 * Configure baud divisors using fractional baud rate if possible.
 */
static void
config_baud_divisors
(
    mss_uart_instance_t * this_uart,
    uint32_t baudrate    
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    
    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        uint32_t baud_value;
        uint32_t baud_value_by_64;
        uint32_t baud_value_by_128;
        uint32_t fractional_baud_value;
        uint32_t pclk_freq;

        this_uart->baudrate = baudrate;

        /* Force the value of the CMSIS global variables holding the various system
          * clock frequencies to be updated. */
        SystemCoreClockUpdate();
        if(this_uart == &g_mss_uart0)
        {
            pclk_freq = g_FrequencyPCLK0;
        }
        else
        {
            pclk_freq = g_FrequencyPCLK1;
        }

        /*
         * Compute baud value based on requested baud rate and PCLK frequency.
         * The baud value is computed using the following equation:
         *      baud_value = PCLK_Frequency / (baud_rate * 16)
         */
        baud_value_by_128 = (8u * pclk_freq) / baudrate;
        baud_value_by_64 = baud_value_by_128 / 2u;
        baud_value = baud_value_by_64 / 64u;
        fractional_baud_value = baud_value_by_64 - (baud_value * 64u);
        fractional_baud_value += baud_value_by_128 - (baud_value * 128u) - (fractional_baud_value * 2);
        
        /* Assert if integer baud value fits in 16-bit. */
        ASSERT(baud_value <= UINT16_MAX);
    
        if(baud_value <= UINT16_MAX)
        {
            if(baud_value > 1u)
            {
                /* 
                 * Use Frational baud rate divisors
                 */
                /* set divisor latch */
                set_bit_reg8(&this_uart->hw_reg->LCR,DLAB);
            
                /* msb of baud value */
                this_uart->hw_reg->DMR = (uint8_t)(baud_value >> 8);
                /* lsb of baud value */
                this_uart->hw_reg->DLR = (uint8_t)baud_value;
            
                /* reset divisor latch */
                clear_bit_reg8(&this_uart->hw_reg->LCR,DLAB);
        
                /* Enable Fractional baud rate */
                set_bit_reg8(&this_uart->hw_reg->MM0,EFBR);
        
                /* Load the fractional baud rate register */
                this_uart->hw_reg->FBR = fractional_baud_value;
            }
            else
            {
                /*
                 * Do NOT use Frational baud rate divisors.
                 */
                /* set divisor latch */
                set_bit_reg8(&this_uart->hw_reg->LCR,DLAB);
            
                /* msb of baud value */
                this_uart->hw_reg->DMR = (uint8_t)(baud_value >> 8u);
                /* lsb of baud value */
                this_uart->hw_reg->DLR = (uint8_t)baud_value;
            
                /* reset divisor latch */
                clear_bit_reg8(&this_uart->hw_reg->LCR,DLAB);
                
                /* Disable Fractional baud rate */
                clear_bit_reg8(&this_uart->hw_reg->MM0,EFBR);
            }
        }
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
#if 0
/*
    There is no need for this function in the driver's API. The baud rate
    configuration is performed as part of MSS_UART_init().
*/

void
MSS_UART_config_integer_baud
(
    mss_uart_instance_t * this_uart,
    uint32_t baudrate    
)
{
    float baud_value;
    uint32_t pclk_freq;
    uint16_t baud_val_i;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    
    if( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        this_uart->baudrate = baudrate;

        /* Force the value of the CMSIS global variables holding the various system
         * clock frequencies to be updated. */
         SystemCoreClockUpdate();
         pclk_freq = g_FrequencyPCLK0;

        /*
         * Compute baud value based on requested baud rate and PCLK frequency.
         * The baud value is computed using the following equation:
         *      baud_value = PCLK_Frequency / (baud_rate * 16)
         * The baud value is treated real number.
         */
        baud_value = (float)( (float)pclk_freq / baudrate);
    
        /* Assert if integer baud value fits in 16-bit and greater than one */
        ASSERT( UINT16_MAX <= ((uint32_t)baud_value) );
    
        if( UINT16_MAX <= ((uint32_t)baud_value) )
        {
            baud_val_i = (uint16_t)baud_value;
        
            if ( baud_val_i & 0x00000008U )
            {
                /* remainder above 0.5 */
                baud_val_i = (baud_val_i >> 4U) + 1U;
            }
            else
            {
                /* remainder below 0.5 */
                baud_val_i = (baud_val_i >> 4U);
            }
        
            /* set divisor latch */
            set_bit_reg8(&this_uart->hw_reg->LCR,DLAB);
        
            /* msb of baud value */
            this_uart->hw_reg->DMR = (uint8_t)(baud_val_i >> 8);
            /* lsb of baud value */
            this_uart->hw_reg->DLR = (uint8_t)baud_val_i;
        
            /* reset divisor latch */
            clear_bit_reg8(&this_uart->hw_reg->LCR,DLAB);
        }
    }
}
#endif

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
void 
MSS_UART_set_usart_mode
(
    mss_uart_instance_t * this_uart,
    mss_uart_usart_mode_t mode
)
{
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( INVALID_SYNC_MODE > mode );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( INVALID_SYNC_MODE > mode ) )
    {
        /* Nothing to do for the baudrate: operates at PCLK / 2 + glitch filter length */
        /* Clear the ESYN bits 2:0 */
        this_uart->hw_reg->MM0 &= ~SYNC_ASYNC_MODE_MASK;
        this_uart->hw_reg->MM0 |= mode;
   }
}

/*******************************************************************************
 * Local Functions
 *******************************************************************************/
/*******************************************************************************
 * Global initialization for all modes
 */
static void global_init
(
    mss_uart_instance_t * this_uart,
    uint32_t baud_rate,
    uint8_t line_config
)
{
    /* The driver expects g_mss_uart0 and g_mss_uart1 to be the only
     * mss_uart_instance_t instances used to identify UART0 and UART1. */
    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if ( this_uart == &g_mss_uart0 )
    {
        this_uart->hw_reg = UART0;
        this_uart->irqn = UART0_IRQn;
        /* reset UART0 */
        SYSREG->SOFT_RST_CR |= SYSREG_MMUART0_SOFTRESET_MASK;
        /* Clear any previously pended UART0 interrupt */
        NVIC_ClearPendingIRQ( UART0_IRQn );
        /* Take UART0 out of reset. */
        SYSREG->SOFT_RST_CR &= ~SYSREG_MMUART0_SOFTRESET_MASK;
    }
    else
    {
        this_uart->hw_reg = UART1;
        this_uart->irqn = UART1_IRQn;
        /* Reset UART1 */
        SYSREG->SOFT_RST_CR |= SYSREG_MMUART1_SOFTRESET_MASK;
        /* Clear any previously pended UART1 interrupt */
        NVIC_ClearPendingIRQ( UART1_IRQn );
        /* Take UART1 out of reset. */
        SYSREG->SOFT_RST_CR &= ~SYSREG_MMUART1_SOFTRESET_MASK;
    }

    /* disable interrupts */
    this_uart->hw_reg->IER = 0U;

    /* FIFO configuration */
    this_uart->hw_reg->FCR = (uint8_t)MSS_UART_FIFO_SINGLE_BYTE;
    /* clear receiver FIFO */
    set_bit_reg8(&this_uart->hw_reg->FCR,CLEAR_RX_FIFO);
    /* clear transmitter FIFO */
    set_bit_reg8(&this_uart->hw_reg->FCR,CLEAR_TX_FIFO);

    /* set default READY mode : Mode 0*/
    /* enable RXRDYN and TXRDYN pins. The earlier FCR write to set the TX FIFO
     * trigger level inadvertently disabled the FCR_RXRDY_TXRDYN_EN bit. */
    set_bit_reg8(&this_uart->hw_reg->FCR,RXRDY_TXRDYN_EN);

    /* disable loopback : local * remote */
    clear_bit_reg8(&this_uart->hw_reg->MCR,LOOP);
    clear_bit_reg8(&this_uart->hw_reg->MCR,RLOOP);

    /* set default TX endian */
    clear_bit_reg8(&this_uart->hw_reg->MM1,E_MSB_TX);
    /* set default RX endian */
    clear_bit_reg8(&this_uart->hw_reg->MM1,E_MSB_RX);

    /* default AFM : disabled */
    clear_bit_reg8(&this_uart->hw_reg->MM2,EAFM);

    /* disable TX time gaurd */
    clear_bit_reg8(&this_uart->hw_reg->MM0,ETTG); 

    /* set default RX timeout */
    clear_bit_reg8(&this_uart->hw_reg->MM0,ERTO); 

    /* disable fractional baud-rate */
    clear_bit_reg8(&this_uart->hw_reg->MM0,EFBR); 

    /* disable single wire mode */
    clear_bit_reg8(&this_uart->hw_reg->MM2,ESWM);

    /* set filter to minimum value */
    this_uart->hw_reg->GLR = 0u;
    /* set default TX time gaurd */
    this_uart->hw_reg->GLR = 0u;
    /* set default RX timeout */
    this_uart->hw_reg->RTO = 0u;
    
    /* 
     * Configure baud rate divisors. This uses the frational baud rate divisor
     * where possible to provide the most accurate baud rat possible.
     */
    config_baud_divisors(this_uart, baud_rate);

    /* set the line control register (bit length, stop bits, parity) */
    this_uart->hw_reg->LCR = line_config;

    /* Instance setup */
    this_uart->baudrate = baud_rate;
    this_uart->lineconfig = line_config;
    this_uart->tx_buff_size = TX_COMPLETE;
    this_uart->tx_buffer = (const uint8_t *)0;
    this_uart->tx_idx = 0U;

    /* Default handlers for MSS UART interrupts */
    this_uart->rx_handler       = NULL_HANDLER;
    this_uart->tx_handler       = NULL_HANDLER;
    this_uart->linests_handler  = NULL_HANDLER;
    this_uart->modemsts_handler = NULL_HANDLER;
    this_uart->rto_handler      = NULL_HANDLER;    
    this_uart->nack_handler     = NULL_HANDLER;   
    this_uart->pid_pei_handler  = NULL_HANDLER;
    this_uart->break_handler    = NULL_HANDLER;    
    this_uart->sync_handler     = NULL_HANDLER;   

    /* Initialize the sticky status */
    this_uart->status = 0U;
}

/***************************************************************************//**
 * Interrupt service routine triggered by any MSS UART interrupt. This routine
 * will call the handler function appropriate to the interrupt from the
 * handlers previously registered with the driver through calls to the
 * MSS_UART_set_*_handler() functions, or it will call the default_tx_handler()
 * function in response to transmit interrupts if MSS_UART_irq_tx() is used to
 * transmit data.
 */
static void
MSS_UART_isr
(
    mss_uart_instance_t * this_uart
)
{
    uint8_t iirf;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );

    if ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) )
    {
        iirf = this_uart->hw_reg->IIR & IIRF_MASK;

        switch ( iirf )
        {
            case IIRF_MODEM_STATUS:  /* Modem status interrupt */
            {
                ASSERT( NULL_HANDLER != this_uart->modemsts_handler );
                if( NULL_HANDLER != this_uart->modemsts_handler )
                {
                   (*(this_uart->modemsts_handler))( this_uart );
                }
            }
            break;

            case IIRF_THRE: /* Transmitter Holding Register Empty */
            {
                ASSERT( NULL_HANDLER != this_uart->tx_handler );
                if ( NULL_HANDLER != this_uart->tx_handler )
                {
                    (*(this_uart->tx_handler))( this_uart );
                }
            }
            break;

            case IIRF_RX_DATA:      /* Received Data Available */
            case IIRF_DATA_TIMEOUT: /* Received Data Timed-out */
            {
                ASSERT( NULL_HANDLER != this_uart->rx_handler );
                if ( NULL_HANDLER != this_uart->rx_handler )
                {
                    (*(this_uart->rx_handler))( this_uart );
                }
            }
            break;

            case IIRF_RX_LINE_STATUS:  /* Line Status Interrupt */
            {
                ASSERT( NULL_HANDLER != this_uart->linests_handler );
                if ( NULL_HANDLER != this_uart->linests_handler )
                {
                   (*(this_uart->linests_handler))( this_uart );
                }
            }
            break;

            case IIRF_MMI:
            {
                /* Identify multimode interrupts and handle */

                /* Receiver time-out interrupt */
                if( read_bit_reg8(&this_uart->hw_reg->IIM,ERTOI) )
                {
                    ASSERT( NULL_HANDLER != this_uart->rto_handler );
                    if( NULL_HANDLER != this_uart->rto_handler )
                    {
                        (*(this_uart->rto_handler))(this_uart);
                    }
                }
                /* NACK interrupt */
                if( read_bit_reg8(&this_uart->hw_reg->IIM,ENACKI) )
                {
                    ASSERT( NULL_HANDLER != this_uart->nack_handler );
                    if( NULL_HANDLER != this_uart->nack_handler )
                    {
                        (*(this_uart->nack_handler))(this_uart);
                    }
                }

                /* PID parity error interrupt */
                if( read_bit_reg8(&this_uart->hw_reg->IIM,EPID_PEI) )
                {
                    ASSERT( NULL_HANDLER != this_uart->pid_pei_handler );
                    if( NULL_HANDLER != this_uart->pid_pei_handler )
                    {
                        (*(this_uart->pid_pei_handler))(this_uart);
                    }
                }

                /* LIN break detection interrupt */
                if( read_bit_reg8(&this_uart->hw_reg->IIM,ELINBI) )
                {
                    ASSERT( NULL_HANDLER != this_uart->break_handler );
                    if( NULL_HANDLER != this_uart->break_handler )
                    {
                        (*(this_uart->break_handler))(this_uart);
                    }
                }

                /* LIN Sync detection interrupt */
                if( read_bit_reg8(&this_uart->hw_reg->IIM,ELINSI) )
                {
                    ASSERT( NULL_HANDLER != this_uart->sync_handler );
                    if( NULL_HANDLER != this_uart->sync_handler )
                    {
                        (*(this_uart->sync_handler))(this_uart);
                    }
                }
                break;
            }

            default:
            {
                ASSERT( INVALID_INTERRUPT );
            }
            break;
        }
    }
}

/***************************************************************************//**
 * See mss_uart.h for details of how to use this function.
 */
static void
default_tx_handler
(
    mss_uart_instance_t * this_uart
)
{
    uint8_t status;

    ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) );
    ASSERT( ( (uint8_t *)0 ) != this_uart->tx_buffer );
    ASSERT( 0U < this_uart->tx_buff_size );

    if( ( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ) &&
        ( ( (uint8_t *)0 ) != this_uart->tx_buffer ) &&
        ( 0U < this_uart->tx_buff_size ) )
    {
        /* Read the Line Status Register and update the sticky record. */
        status = this_uart->hw_reg->LSR;
        this_uart->status |= status;

        /*
         * This function should only be called as a result of a THRE interrupt.
         * Verify that this is true before proceeding to transmit data.
         */
        if ( status & MSS_UART_THRE )
        {
            uint32_t i;
            uint32_t fill_size = TX_FIFO_SIZE;
            uint32_t tx_remain = this_uart->tx_buff_size - this_uart->tx_idx;

            /* Calculate the number of bytes to transmit. */
            if ( tx_remain < TX_FIFO_SIZE )
            {
                fill_size = tx_remain;
            }

            /* Fill the TX FIFO with the calculated the number of bytes. */
            for ( i = 0U; i < fill_size; ++i )
            {
                /* Send next character in the buffer. */
                this_uart->hw_reg->THR = this_uart->tx_buffer[this_uart->tx_idx];
                ++this_uart->tx_idx;
            }
        }

        /* Flag Tx as complete if all data has been pushed into the Tx FIFO. */
        if ( this_uart->tx_idx == this_uart->tx_buff_size )
        {
            this_uart->tx_buff_size = TX_COMPLETE;
            /* disables TX interrupt */
            clear_bit_reg8(&this_uart->hw_reg->IER,ETBEI);
        }
    }
}

#ifdef __cplusplus
}
#endif
