/*
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "uart_imx.h"

/*******************************************************************************
 * Code
 ******************************************************************************/

/*******************************************************************************
 * Initialization and Configuration functions
 ******************************************************************************/
/*FUNCTION**********************************************************************
 *
 * Function Name : UART_Init
 * Description   : This function initializes the module according to uart
 *                 initialize structure.
 *
 *END**************************************************************************/
void UART_Init(UART_Type* base, const uart_init_config_t* initConfig)
{
    assert(initConfig);

    /* Disable UART Module. */
    UART_UCR1_REG(base) &= ~UART_UCR1_UARTEN_MASK;

    /* Reset UART register to its default value. */
    UART_Deinit(base);

    /* Set UART data word length, stop bit count, parity mode and communication
     * direction according to uart init struct, disable RTS hardware flow
     * control. */
    UART_UCR2_REG(base) |= (initConfig->wordLength |
                            initConfig->stopBitNum |
                            initConfig->parity     |
                            initConfig->direction  |
                            UART_UCR2_IRTS_MASK);

    /* For imx family device, UARTs are used in MUXED mode,
     * so that this bit should always be set.*/
    UART_UCR3_REG(base) |= UART_UCR3_RXDMUXSEL_MASK;

    /* Set BaudRate according to uart initialize struct. */
    /* Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)) */
    UART_SetBaudRate(base, initConfig->clockRate, initConfig->baudRate);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_Deinit
 * Description   : This function reset Uart module register content to its
 *                 default value.
 *
 *END**************************************************************************/
void UART_Deinit(UART_Type* base)
{
    /* Disable UART Module */
    UART_UCR1_REG(base) &= ~UART_UCR1_UARTEN_MASK;

    /* Reset UART Module Register content to default value */
    UART_UCR1_REG(base)  = 0x0;
    UART_UCR2_REG(base)  = UART_UCR2_SRST_MASK;
    UART_UCR3_REG(base)  = UART_UCR3_DSR_MASK |
                           UART_UCR3_DCD_MASK |
                           UART_UCR3_RI_MASK;
    UART_UCR4_REG(base)  = UART_UCR4_CTSTL(32);
    UART_UFCR_REG(base)  = UART_UFCR_TXTL(2) | UART_UFCR_RXTL(1);
    UART_UESC_REG(base)  = UART_UESC_ESC_CHAR(0x2B);
    UART_UTIM_REG(base)  = 0x0;
    UART_ONEMS_REG(base) = 0x0;
    UART_UTS_REG(base)   = UART_UTS_TXEMPTY_MASK | UART_UTS_RXEMPTY_MASK;
    UART_UMCR_REG(base)  = 0x0;

    /* Reset the transmit and receive state machines, all FIFOs and register
     * USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD and UTS[6-3]. */
    UART_UCR2_REG(base) &= ~UART_UCR2_SRST_MASK;
    while (!(UART_UCR2_REG(base) & UART_UCR2_SRST_MASK));
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetBaudRate
 * Description   : 
 *
 *END**************************************************************************/
void UART_SetBaudRate(UART_Type* base, uint32_t clockRate, uint32_t baudRate)
{
    uint32_t numerator;
    uint32_t denominator;
    uint32_t divisor;
    uint32_t refFreqDiv;
    uint32_t divider = 1;

    /* get the approximately maximum divisor */
    numerator = clockRate;
    denominator = baudRate << 4;
    divisor = 1;

    while (denominator != 0)
    {
        divisor = denominator;
        denominator = numerator % denominator;
        numerator = divisor;
    }

    numerator = clockRate / divisor;
    denominator = (baudRate << 4) / divisor;

    /* numerator ranges from 1 ~ 7 * 64k */
    /* denominator ranges from 1 ~ 64k */
    if ((numerator > (UART_UBIR_INC_MASK * 7)) ||
        (denominator > UART_UBIR_INC_MASK))
    {
        uint32_t m = (numerator - 1) / (UART_UBIR_INC_MASK * 7) + 1;
        uint32_t n = (denominator - 1) / UART_UBIR_INC_MASK + 1;
        uint32_t max = m > n ? m : n;
        numerator /= max;
        denominator /= max;
        if (0 == numerator)
            numerator = 1;
        if (0 == denominator)
            denominator = 1;
    }
    divider = (numerator - 1) / UART_UBIR_INC_MASK + 1;

    switch (divider)
    {
        case 1:
            refFreqDiv = 0x05;
            break;
        case 2:
            refFreqDiv = 0x04;
            break;
        case 3:
            refFreqDiv = 0x03;
            break;
        case 4:
            refFreqDiv = 0x02;
            break;
        case 5:
            refFreqDiv = 0x01;
            break;
        case 6:
            refFreqDiv = 0x00;
            break;
        case 7:
            refFreqDiv = 0x06;
            break;
        default:
        refFreqDiv = 0x05;
    }

    UART_UFCR_REG(base) &= ~UART_UFCR_RFDIV_MASK;
    UART_UFCR_REG(base) |= UART_UFCR_RFDIV(refFreqDiv);
    UART_UBIR_REG(base) = UART_UBIR_INC(denominator - 1);
    UART_UBMR_REG(base) = UART_UBMR_MOD(numerator / divider - 1);
    UART_ONEMS_REG(base) = UART_ONEMS_ONEMS(clockRate/(1000 * divider));
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetInvertCmd
 * Description   : This function is used to set the polarity of UART signal.
 *                 The polarity of Tx and Rx can be set separately.
 *
 *END**************************************************************************/
void UART_SetInvertCmd(UART_Type* base, uint32_t direction, bool invert)
{
    assert((direction & uartDirectionTx) || (direction & uartDirectionRx));

    if (invert)
    {
        if (direction & UART_UCR2_RXEN_MASK)
            UART_UCR4_REG(base) |= UART_UCR4_INVR_MASK;
        if (direction & UART_UCR2_TXEN_MASK)
            UART_UCR3_REG(base) |= UART_UCR3_INVT_MASK;
    }
    else
    {
        if (direction & UART_UCR2_RXEN_MASK)
            UART_UCR4_REG(base) &= ~UART_UCR4_INVR_MASK;
        if (direction & UART_UCR2_TXEN_MASK)
            UART_UCR3_REG(base) &= ~UART_UCR3_INVT_MASK;
    }
}

/*******************************************************************************
 * Low Power Mode functions
 ******************************************************************************/
/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetDozeMode
 * Description   : This function is used to set UART enable condition in the
 *                 DOZE state.
 *
 *END**************************************************************************/
void UART_SetDozeMode(UART_Type* base, bool enable)
{
    if (enable)
        UART_UCR1_REG(base) &= UART_UCR1_DOZE_MASK;
    else
        UART_UCR1_REG(base) |= ~UART_UCR1_DOZE_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetLowPowerMode
 * Description   : This function is used to set UART enable condition of the
 *                 UART low power feature.
 *
 *END**************************************************************************/
void UART_SetLowPowerMode(UART_Type* base, bool enable)
{
    if (enable)
        UART_UCR4_REG(base) &= ~UART_UCR4_LPBYP_MASK;
    else
        UART_UCR4_REG(base) |= UART_UCR4_LPBYP_MASK;
}

/*******************************************************************************
 * Interrupt and Flag control functions
 ******************************************************************************/
/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetIntCmd
 * Description   : This function is used to set the enable condition of
 *                 specific UART interrupt source. The available interrupt
 *                 source can be select from uart_int_source enumeration.
 *
 *END**************************************************************************/
void UART_SetIntCmd(UART_Type* base, uint32_t intSource, bool enable)
{
    volatile uint32_t* uart_reg = 0;
    uint32_t uart_mask = 0;

    uart_reg = (uint32_t *)((uint32_t)base + (intSource >> 16));
    uart_mask = (1 << (intSource & 0x0000FFFF));

    if (enable)
        *uart_reg |= uart_mask;
    else
        *uart_reg &= ~uart_mask;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_GetStatusFlag
 * Description   : This function is used to get the current status of specific
 *                 UART status flag. The available status flag can be select
 *                 from uart_status_flag & uart_interrupt_flag enumeration.
 *
 *END**************************************************************************/
bool UART_GetStatusFlag(UART_Type* base, uint32_t flag)
{
    volatile uint32_t* uart_reg = 0;

    uart_reg = (uint32_t *)((uint32_t)base + (flag >> 16));
    return (bool)((*uart_reg >> (flag & 0x0000FFFF)) & 0x1);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_ClearStatusFlag
 * Description   : This function is used to get the current status
 *                 of specific UART status flag. The available status
 *                 flag can be select from uart_status_flag &
 *                 uart_interrupt_flag enumeration.
 *
 *END**************************************************************************/
void UART_ClearStatusFlag(UART_Type* base, uint32_t flag)
{
    volatile uint32_t* uart_reg = 0;
    uint32_t uart_mask = 0;

    uart_reg = (uint32_t *)((uint32_t)base + (flag >> 16));
    uart_mask = (1 << (flag & 0x0000FFFF));

    /* write 1 to clear. */
    *uart_reg = uart_mask;
}

/*******************************************************************************
 * DMA control functions
 ******************************************************************************/
/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetDmaCmd
 * Description   : This function is used to set the enable condition of
 *                 specific UART DMA source. The available DMA
 *                 source can be select from uart_dma_source enumeration.
 *
 *END**************************************************************************/
void UART_SetDmaCmd(UART_Type* base, uint32_t dmaSource, bool enable)
{
    volatile uint32_t* uart_reg = 0;
    uint32_t uart_mask = 0;

    uart_reg = (uint32_t *)((uint32_t)base + (dmaSource >> 16));
    uart_mask = (1 << (dmaSource & 0x0000FFFF));
    if (enable)
        *uart_reg |= uart_mask;
    else
        *uart_reg &= ~uart_mask;
}

/*******************************************************************************
 * Hardware Flow control and Modem Signal functions
 ******************************************************************************/
/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetRtsFlowCtrlCmd
 * Description   : This function is used to set the enable condition of RTS
 *                 Hardware flow control.
 *
 *END**************************************************************************/
void UART_SetRtsFlowCtrlCmd(UART_Type* base, bool enable)
{
    if (enable)
        UART_UCR2_REG(base) &= ~UART_UCR2_IRTS_MASK;
    else
        UART_UCR2_REG(base) |= UART_UCR2_IRTS_MASK; 
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetCtsFlowCtrlCmd
 * Description   : This function is used to set the enable condition of CTS
 *                 auto control. if CTS control is enabled, the CTS_B pin will
 *                 be controlled by the receiver, otherwise the CTS_B pin will
 *                 controlled by UART_CTSPinCtrl function. 
 *
 *END**************************************************************************/
void UART_SetCtsFlowCtrlCmd(UART_Type* base, bool enable)
{
    if (enable)
        UART_UCR2_REG(base) |= UART_UCR2_CTSC_MASK;
    else
        UART_UCR2_REG(base) &= ~UART_UCR2_CTSC_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetCtsPinLevel
 * Description   : This function is used to control the CTS_B pin state when 
 *                 auto CTS control is disabled.
 *                 The CTS_B pin is low (active)
 *                 The CTS_B pin is high (inactive)
 *
 *END**************************************************************************/
void UART_SetCtsPinLevel(UART_Type* base, bool active)
{
    if (active)
        UART_UCR2_REG(base) |= UART_UCR2_CTS_MASK;
    else
        UART_UCR2_REG(base) &= ~UART_UCR2_CTS_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetModemMode
 * Description   : This function is used to set the role(DTE/DCE) of UART module
 *                 in RS-232 communication.
 *
 *END**************************************************************************/
void UART_SetModemMode(UART_Type* base, uint32_t mode)
{
    assert((uartModemModeDce & uartModemModeDce) || (uartModemModeDce & uartModemModeDte));

    if (uartModemModeDce == mode)
        UART_UFCR_REG(base) &= ~UART_UFCR_DCEDTE_MASK;
    else
        UART_UFCR_REG(base) |= UART_UFCR_DCEDTE_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetDtrPinLevel
 * Description   : This function is used to set the pin state of 
 *                 DSR pin(for DCE mode) or DTR pin(for DTE mode) for the
 *                 modem interface.
 *
 *END**************************************************************************/
void UART_SetDtrPinLevel(UART_Type* base, bool active)
{
    if (active)
        UART_UCR3_REG(base) |= UART_UCR3_DSR_MASK;
    else
        UART_UCR3_REG(base) &= ~UART_UCR3_DSR_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetDcdPinLevel
 * Description   : This function is used to set the pin state of 
 *                 DCD pin. THIS FUNCTION IS FOR DCE MODE ONLY.
 *
 *END**************************************************************************/
void UART_SetDcdPinLevel(UART_Type* base, bool active)
{
    if (active)
        UART_UCR3_REG(base) |= UART_UCR3_DCD_MASK;
    else
        UART_UCR3_REG(base) &= ~UART_UCR3_DCD_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetRiPinLevel
 * Description   : This function is used to set the pin state of 
 *                 RI pin. THIS FUNCTION IS FOR DCE MODE ONLY.
 *
 *END**************************************************************************/
void UART_SetRiPinLevel(UART_Type* base, bool active)
{
    if (active)
        UART_UCR3_REG(base) |= UART_UCR3_RI_MASK;
    else
        UART_UCR3_REG(base) &= ~UART_UCR3_RI_MASK;
}

/*******************************************************************************
 * Multiprocessor and RS-485 functions
 ******************************************************************************/
/*FUNCTION**********************************************************************
 *
 * Function Name : UART_Putchar9
 * Description   : This function is used to send 9 Bits length data in
 *                 RS-485 Multidrop mode.
 *
 *END**************************************************************************/
void UART_Putchar9(UART_Type* base, uint16_t data)
{
    assert(data <= 0x1FF);

    if (data & 0x0100)
        UART_UMCR_REG(base) |= UART_UMCR_TXB8_MASK;
    else
        UART_UMCR_REG(base) &= ~UART_UMCR_TXB8_MASK;
    UART_UTXD_REG(base) = (data & UART_UTXD_TX_DATA_MASK);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_Getchar9
 * Description   : This functions is used to receive 9 Bits length data in
 *                 RS-485 Multidrop mode.
 *
 *END**************************************************************************/
uint16_t UART_Getchar9(UART_Type* base)
{
    uint16_t rxData = UART_URXD_REG(base);

    if (rxData & UART_URXD_PRERR_MASK)
    {
        rxData = (rxData & 0x00FF) | 0x0100;
    }
    else
    {
        rxData &= 0x00FF;
    }

    return rxData;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetMultidropMode
 * Description   : This function is used to set the enable condition of 
 *                 9-Bits data or Multidrop mode.
 *
 *END**************************************************************************/
void UART_SetMultidropMode(UART_Type* base, bool enable)
{
    if (enable)
        UART_UMCR_REG(base) |= UART_UMCR_MDEN_MASK;
    else
        UART_UMCR_REG(base) &= ~UART_UMCR_MDEN_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetSlaveAddressDetectCmd
 * Description   : This function is used to set the enable condition of
 *                 Automatic Address Detect Mode.
 *
 *END**************************************************************************/
void UART_SetSlaveAddressDetectCmd(UART_Type* base, bool enable)
{
    if (enable)
        UART_UMCR_REG(base) |= UART_UMCR_SLAM_MASK;
    else
        UART_UMCR_REG(base) &= ~UART_UMCR_SLAM_MASK;
}

/*******************************************************************************
 * IrDA control functions
 ******************************************************************************/
/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetIrDACmd
 * Description   : This function is used to set the enable condition of
 *                 IrDA Mode.
 *
 *END**************************************************************************/
void UART_SetIrDACmd(UART_Type* base, bool enable)
{
    if (enable)
        UART_UCR1_REG(base) |= UART_UCR1_IREN_MASK;
    else
        UART_UCR1_REG(base) &= ~UART_UCR1_IREN_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetIrDAVoteClock
 * Description   : This function is used to set the clock for the IR pulsed
 *                 vote logic. The available clock can be select from
 *                 uart_irda_vote_clock enumeration.
 *
 *END**************************************************************************/
void UART_SetIrDAVoteClock(UART_Type* base, uint32_t voteClock)
{
    assert((voteClock == uartIrdaVoteClockSampling) || \
           (voteClock == uartIrdaVoteClockReference));

    if (uartIrdaVoteClockSampling == voteClock)
        UART_UCR4_REG(base) |= UART_UCR4_IRSC_MASK;
    else
        UART_UCR4_REG(base) &= ~UART_UCR4_IRSC_MASK;
}

/*******************************************************************************
 * Misc. functions
 ******************************************************************************/
/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetAutoBaudRateCmd
 * Description   : This function is used to set the enable condition of
 *                 Automatic Baud Rate Detection feature.
 *
 *END**************************************************************************/
void UART_SetAutoBaudRateCmd(UART_Type* base, bool enable)
{
    if (enable)
        UART_UCR1_REG(base) |= UART_UCR1_ADBR_MASK;
    else
        UART_UCR1_REG(base) &= ~UART_UCR1_ADBR_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SendBreakChar
 * Description   : This function is used to send BREAK character.It is 
 *                 important that SNDBRK is asserted high for a sufficient
 *                 period of time to generate a valid BREAK. 
 *
 *END**************************************************************************/
void UART_SendBreakChar(UART_Type* base, bool active)
{
    if (active)
        UART_UCR1_REG(base) |= UART_UCR1_SNDBRK_MASK;
    else
        UART_UCR1_REG(base) &= ~UART_UCR1_SNDBRK_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : UART_SetEscapeDecectCmd
 * Description   : This function is used to set the enable condition of
 *                 Escape Sequence Detection feature.
 *
 *END**************************************************************************/
void UART_SetEscapeDecectCmd(UART_Type* base, bool enable)
{
    if (enable)
        UART_UCR2_REG(base) |= UART_UCR2_ESCEN_MASK;
    else
        UART_UCR2_REG(base) &= ~UART_UCR2_ESCEN_MASK;
}

/*******************************************************************************
 * EOF
 ******************************************************************************/
